package cm.tl.pms.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 列表比较工具类。
 */
public class ListComparator {

    /**
     * 比较两个列表中的对象，并返回修改前后的字段值以及每条数据对应的唯一ID。
     *
     * @param originList      原始列表
     * @param newList         新列表
     * @param fieldNames      需要比较的字段名称列表
     * @param uniqueFieldName 唯一字段名称
     * @param <T>             列表中的对象类型
     * @return 修改记录列表
     */
    public static <T> List<ChangeInfo> compareLists(List<? extends T> originList, List<? extends T> newList, List<String> targetNames, List<String> fieldNames, String uniqueFieldName) {
        if (originList == null || newList == null || uniqueFieldName == null || fieldNames == null) {
            throw new IllegalArgumentException("比较的新旧集合 / 比较目标字段集合  cannot be null");
        }
        //比较默认值为id
        uniqueFieldName = StringUtils.isBlank(uniqueFieldName) ? "id" : uniqueFieldName;
        Map<Object, T> originMap = toMap(originList, uniqueFieldName);
        Map<Object, T> newMap = toMap(newList, uniqueFieldName);

        List<ChangeInfo> changeInfos = new ArrayList<>();

        for (Object key : newMap.keySet()) {
            T originItem = originMap.get(key);
            T newItem = newMap.get(key);

            if (originItem != null && newItem != null) {
                List<ChangeRecord> changes = getChanges(originItem, newItem, targetNames, fieldNames);
                if (!changes.isEmpty()) {
                    changeInfos.add(new ChangeInfo(key, changes));
                }
            }
        }

        return changeInfos;
    }

    /**
     * 将列表转换为基于唯一字段的映射。
     *
     * @param list            列表
     * @param uniqueFieldName 唯一字段名称
     * @param <T>             列表中的对象类型
     * @return 映射
     */
    private static <T> Map<Object, T> toMap(List<? extends T> list, String uniqueFieldName) {
        return list.stream()
                .collect(Collectors.toMap(
                        item -> getFieldValue(item, uniqueFieldName),
                        item -> item
                ));
    }

    /**
     * 获取对象的指定字段值。
     *
     * @param item      对象
     * @param fieldName 字段名称
     * @param <T>       对象类型
     * @return 字段值
     */
//    private static <T> Object getFieldValue(T item, String fieldName) {
//        try {
//            System.out.println("Class: " + item.getClass().getName() + ", Field: " + fieldName);
//            Field field = item.getClass().getDeclaredField(fieldName);
//            field.setAccessible(true);
//            return field.get(item);
//        } catch (NoSuchFieldException | IllegalAccessException e) {
//            throw new RuntimeException("Error accessing field: " + fieldName, e);
//        }
//    }
    private static <T> Object getFieldValue(T item, String fieldName) {
        if (item == null) {
            throw new IllegalArgumentException("Item cannot be null");
        }
        try {
            Class<?> clazz = item.getClass();
//            System.out.println("Class: " + clazz.getName() + ", Field: " + fieldName);

            if (item instanceof Map) {
                // 如果 item 是 Map 类型，直接从 Map 中获取字段值
                Map<?, ?> map = (Map<?, ?>) item;
                return map.get(fieldName);
            } else {
                // 如果 item 不是 Map 类型，使用反射获取字段值
                Field field = FieldUtils.getField(clazz, fieldName, true);
                if (field == null) {
                    throw new RuntimeException("Field not found: " + fieldName + " in class: " + clazz.getName());
                }
                return FieldUtils.readField(field, item, true);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Error accessing field: " + fieldName, e);
        }
    }


    /**
     * 获取两个对象之间的字段变化记录。
     *
     * @param originItem 原始对象
     * @param newItem    新对象
     * @param fieldNames 需要比较的字段名称列表
     * @param <T>        对象类型
     * @return 字段变化记录列表
     */
    private static <T> List<ChangeRecord> getChanges(T originItem, T newItem, List<String> targetNames, List<String> fieldNames) {
        List<ChangeRecord> changes = new ArrayList<>();
        for (int i = 0; i < fieldNames.size(); i++) {
            String fieldName = fieldNames.get(i);
            String targetName = targetNames.get(i);
            try {
                Object originValue = getFieldValue(originItem, fieldName);
                Object newValue = getFieldValue(newItem, fieldName);
                Object targetNameValue = getFieldValue(originItem, targetName);

                if (!Objects.equals(originValue, newValue)) {
                    changes.add(new ChangeRecord((String) targetNameValue, originValue, newValue, originItem, newItem));
                }
            } catch (Exception e) {
                throw new RuntimeException("Error accessing field: " + fieldName, e);
            }

        }
        return changes;
    }


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ChangeInfo {
        /**
         * 唯一字段
         */
        private Object uniqueId;

        /**
         * 变化的集合
         */
        private List<ChangeRecord> changes;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ChangeRecord {
        /**
         * 字段名称
         */
        private String fieldName;

        /**
         * 旧值
         */
        private Object oldValue;

        /**
         * 新值
         */
        private Object newValue;

        /**
         * 原始对象
         */
        private Object originItem;

        /**
         * 新对象
         */
        private Object newItem;
    }
}
